package com.comleader.operator.service;

import static com.comleader.operator.common.constant.Constant.*;
import static com.comleader.operator.common.enums.OperatorInspectTypeEnum.*;
import static com.comleader.operator.common.response.ResultEnum.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.excel.util.ListUtils;
import com.comleader.operator.common.enums.InspectStatusEnum;
import com.comleader.operator.common.util.OperatorObjectUtil;
import com.comleader.operator.core.exception.BusinessException;
import com.comleader.operator.pojo.dto.InspectDataDTO;
import com.comleader.operator.pojo.entity.*;
import com.comleader.operator.pojo.param.OperatorInspectParam;
import com.comleader.operator.service.strategy.InspectStartegy;
import com.comleader.operator.service.strategy.InspectStrategyFactory;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.CharsetDetector;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @Dscription 运营商检查Service
 * @Author 连新景
 * @Date 2024/07/26 17:04
 */
@Slf4j
@Service
public class OperatorInspectService {

    @Resource
    private FileImportRecordService fileImportRecordService;

    @Resource
    private InspectRecordService inspectRecordService;

    private static final Map<Integer, InspectDataDTO> map = new HashMap<>(3);

    @PostConstruct
    public void init() {
        map.put(PERSONAL_USER.getType(), new InspectDataDTO(PersonalUserEntity.class, SpringUtil.getBean(PersonalUserService.class), PERSONAL_USER_FIELD_NUM));
        map.put(IOV_CARD.getType(), new InspectDataDTO(IovCardEntity.class, SpringUtil.getBean(IovCardService.class), IOC_CARD_FIELD_NUM));
        map.put(SMS_PORT.getType(), new InspectDataDTO(SmsPortEntity.class, SpringUtil.getBean(SmsPortService.class), SMS_PORT_FIELD_NUM));
    }

    private void validateFile(MultipartFile file) throws IOException {

        if (file.isEmpty() || file.getSize() == 0) {
            throw new BusinessException(FILE_EMPTY);
        }
        Charset charset = CharsetDetector.detect(file.getInputStream(), null);
        log.info("{}读取到编码格式为：{}", file.getName(), charset.name());
        // 校验不准确，先不用了
        /* Charset charset = CharsetDetector.detect(file.getInputStream(), null);
        if (charset == null || !charset.name().contains("UTF")) {
           throw new BusinessException(FILE_NOT_UTF8);
        }*/
    }

    /**
     * @Description 读取检查文件，并入库
     * @Auhtor 连新景
     * @Date 2024/07/29 11:20
     * @param multipartFile
     * @param bussinessType
     */
    @Transactional(rollbackFor = Exception.class)
    public int readFileAndSaveDb(MultipartFile multipartFile, Integer bussinessType) throws IOException {
        log.info("开始读取检查文件【{}】", multipartFile.getOriginalFilename());
        validateFile(multipartFile);
        InspectDataDTO inspectDataDTO = map.get(bussinessType);
        if (inspectDataDTO == null) {
            throw new BusinessException(INVALID_BUSINESS_TYPE);
        }
        String line;
        List<String> list;
        Object t;
        List<Object> entityList = ListUtils.newArrayListWithExpectedSize(BATCH_SIZE);
        int lineNum = 1;
        File file = FileUtil.newFile(IMPORT_FILE_PATH + multipartFile.getOriginalFilename());
        FileUtil.writeFromStream(multipartFile.getInputStream(), file);
        FileImportRecordEntity fileImportRecord = FileImportRecordEntity.builder().filePath(IMPORT_FILE_PATH + multipartFile.getOriginalFilename())
            .businessType(bussinessType).createTime(LocalDateTime.now()).build();
        fileImportRecordService.save(fileImportRecord);
        BufferedReader reader = FileUtil.getReader(file, CharsetUtil.CHARSET_GBK);
        // BufferedReader reader = FileUtil.getUtf8Reader(file);
        try {
            while (true) {
                line = reader.readLine();
                if (StrUtil.isBlank(line)) {
                    break;
                }
                list = StrUtil.split(line, "||");
                if (list.size() != inspectDataDTO.getFieldNum()) {
                    lineNum++;
                    continue;
                }
                t = OperatorObjectUtil.createObj(list, inspectDataDTO.getClazz());
                // 短信数据有行号了
                if (!bussinessType.equals(SMS_PORT.getType())) {
                    ReflectUtil.setFieldValue(t, "lineNum", lineNum);
                }
                entityList.add(t);
                if (entityList.size() == BATCH_SIZE) {
                    inspectDataDTO.getService().saveBatch(entityList, BATCH_SIZE);
                    entityList = ListUtils.newArrayListWithExpectedSize(BATCH_SIZE);
                }
                lineNum++;
            }
        } finally {
            IoUtil.close(reader);
        }
        if (CollectionUtil.isNotEmpty(entityList)) {
            inspectDataDTO.getService().saveBatch(entityList);
        }
        log.info("检查文件【{}】入库结束", file.getName());
        return fileImportRecord.getId();
    }

    /**
     * @Description 删除所有数据
     * @Auhtor 连新景
     * @Date 2024/07/29 10:14
     * @param businessType
     * @return boolean
     */
    @Transactional(rollbackFor = Exception.class)
    public int deleteImportData(Integer businessType, Integer importRecordId) {
        FileImportRecordEntity fileImportRecord = fileImportRecordService.getById(importRecordId);
        File file = new File(fileImportRecord.getFilePath());
        file.delete();
        // 清空对应业务下表中所有数据
        InspectDataDTO inspectDataDTO = map.get(businessType);
        return inspectDataDTO.getService().truncate(inspectDataDTO.getClazz());
    }

    public void inspect(OperatorInspectParam param) {
        FileImportRecordEntity fileImportRecord = fileImportRecordService.getById(param.getImportRecordId());
        if (fileImportRecord == null) {
            log.error("查询不到文件导入记录");
            throw new BusinessException(FILE_NOT_EXIST);
        }
        String filePath = fileImportRecord.getFilePath();
        File file = new File(fileImportRecord.getFilePath());
        if (!file.exists()) {
            log.error(FILE_NOT_EXIST.getMsg() + ":" + filePath);
            throw new BusinessException(FILE_NOT_EXIST);
        }
        String reportFileDir = REPORT_FILE_PATH + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + StrUtil.SLASH;
        FileUtil.mkdir(new File(reportFileDir));
        String fileName = FileUtil.getName(filePath);
        InspectRecordEntity inspectRecord =
            InspectRecordEntity.builder().operatorName(param.getOperatorName()).fileName(fileName).type(param.getType()).inspectStartTime(LocalDateTime.now())
                .inspectStatus(InspectStatusEnum.UNDERWAY.getStatus()).reportFileDir(reportFileDir).importRecordId(param.getImportRecordId()).build();
        inspectRecordService.save(inspectRecord);
        // 异步进行检查
        ThreadUtil.execute(() -> {
            log.info("开始检查文件【{}】", fileName);
            InspectStartegy inspectStartegy = InspectStrategyFactory.getInstance().get(param.getType());
            inspectStartegy.inspect(inspectRecord, filePath);
            deleteImportData(param.getType(), param.getImportRecordId());
            inspectRecordService.updateStatusAndFinishTime(inspectRecord.getId(), InspectStatusEnum.FINISHED);
            log.info("【{}】检查结束", fileName);
        });
    }

}
